package com.kongjs.application.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kongjs.application.cache.UserCacheManagerImpl;
import com.kongjs.application.convert.UsersConverter;
import com.kongjs.application.mapper.UsersMapper;
import com.kongjs.application.model.dto.*;
import com.kongjs.application.model.entity.UserDetail;
import com.kongjs.application.model.entity.UserGroup;
import com.kongjs.application.model.entity.UserRole;
import com.kongjs.application.model.entity.Users;
import com.kongjs.application.service.IUserDetailService;
import com.kongjs.application.service.IUserGroupService;
import com.kongjs.application.service.IUserRoleService;
import com.kongjs.application.service.IUsersService;
import com.kongjs.application.util.PageUtil;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Transactional
@Service
public class UsersServiceImpl extends ServiceImpl<UsersMapper, Users> implements IUsersService {
    @Resource
    private UsersConverter usersConverter;
    @Resource
    private IUserDetailService userDetailService;
    @Resource
    private IUserRoleService userRoleService;
    @Resource
    private IUserGroupService userGroupService;

    @Override
    public boolean save(SaveUserDTO dto) {
        Users users = usersConverter.toEntity(dto);
        if (!CollectionUtils.isEmpty(dto.getRoleIds())) {
            List<UserRole> userRoles = new ArrayList<>();
            for (Long roleId : dto.getRoleIds()) {
                UserRole userRole = new UserRole();
                userRole.setUserId(users.getId());
                userRole.setRoleId(roleId);
                userRoles.add(userRole);
            }
            userRoleService.saveBatch(userRoles);
        }
        if (!CollectionUtils.isEmpty(dto.getGroupIds())) {
            List<UserGroup> userGroups = new ArrayList<>();
            for (Long groupId : dto.getGroupIds()) {
                UserGroup userGroup = new UserGroup();
                userGroup.setUserId(users.getId());
                userGroup.setGroupId(groupId);
                userGroups.add(userGroup);
            }
            userGroupService.saveBatch(userGroups);
        }
        save(users);
        UserCacheDTO cache = usersConverter.toCache(users);
        cache.setRoleIds(dto.getRoleIds());
        UserCacheManagerImpl.getInstance().saveUserCache(cache);
        return true;
    }

    @Override
    public boolean remove(Long id) {
        removeById(id);
        userDetailService.lambdaUpdate()
                .eq(UserDetail::getUserId, id)
                .remove();
        userRoleService.lambdaUpdate()
                .eq(UserRole::getUserId, id)
                .remove();
        userGroupService.lambdaUpdate()
                .eq(UserGroup::getUserId, id)
                .remove();
        UserCacheManagerImpl.getInstance().removeUserCache(id);
        return true;
    }

    @Override
    public boolean update(UpdateUserDTO dto) {
        Users users = usersConverter.toEntity(dto);
        userRoleService.lambdaUpdate()
                .eq(UserRole::getUserId, dto.getId())
                .remove();
        userGroupService.lambdaUpdate()
                .eq(UserGroup::getUserId, dto.getId())
                .remove();
        if (!CollectionUtils.isEmpty(dto.getRoleIds())) {
            List<UserRole> userRoles = new ArrayList<>();
            for (Long roleId : dto.getRoleIds()) {
                UserRole userRole = new UserRole();
                userRole.setUserId(users.getId());
                userRole.setRoleId(roleId);
                userRoles.add(userRole);
            }
            userRoleService.saveBatch(userRoles);
        }
        if (!CollectionUtils.isEmpty(dto.getGroupIds())) {
            List<UserGroup> userGroups = new ArrayList<>();
            for (Long groupId : dto.getGroupIds()) {
                UserGroup userGroup = new UserGroup();
                userGroup.setUserId(users.getId());
                userGroup.setGroupId(groupId);
                userGroups.add(userGroup);
            }
            userGroupService.saveBatch(userGroups);
        }
        updateById(users);
        UserCacheDTO cache = usersConverter.toCache(users);
        cache.setRoleIds(dto.getRoleIds());
        UserCacheManagerImpl.getInstance().saveUserCache(cache);
        return true;
    }

    @Override
    public PageDTO<QueryUserDTO> page(PageQueryDTO dto, QueryUserParamDTO paramDTO) {
        List<Long> queryUserIds = new ArrayList<>();
        List<Long> queryGroupIds = new ArrayList<>();
        if (!CollectionUtils.isEmpty(paramDTO.getGroupIds())) {
            List<UserGroup> userGroups = userGroupService.lambdaQuery().in(UserGroup::getGroupId, paramDTO.getGroupIds()).list();
            queryUserIds = userGroups.stream().map(UserGroup::getUserId).toList();
            queryGroupIds = userGroups.stream().map(UserGroup::getGroupId).toList();
            if (CollectionUtils.isEmpty(queryUserIds)) {
                PageDTO<QueryUserDTO> pageDTO = new PageDTO<>();
                pageDTO.setPageNum(dto.getPageNum());
                pageDTO.setPageSize(dto.getPageSize());
                pageDTO.setTotalPages(0L);
                pageDTO.setTotal(0L);
                return pageDTO;
            }
        }
        Page<Users> page;
        if (!ObjectUtils.isEmpty(paramDTO)) {
            page = lambdaQuery()
                    .in(!CollectionUtils.isEmpty(queryUserIds), Users::getId, queryUserIds)
                    .eq(!ObjectUtils.isEmpty(paramDTO.getId()), Users::getId, paramDTO.getId())
                    .like(StringUtils.hasText(paramDTO.getNickname()), Users::getNickname, paramDTO.getNickname())
                    .like(StringUtils.hasText(paramDTO.getUsername()), Users::getUsername, paramDTO.getUsername())
                    .eq(!ObjectUtils.isEmpty(paramDTO.getEnabled()), Users::getEnabled, paramDTO.getEnabled())
                    .between(!ObjectUtils.isEmpty(paramDTO.getStartDate()) && !ObjectUtils.isEmpty(paramDTO.getEndDate()), Users::getCreateDate, paramDTO.getStartDate(), paramDTO.getEndDate())
                    .orderByDesc(Users::getUpdateDate)
                    .page(Page.of(dto.getPageNum(), dto.getPageSize()));
        } else {
            page = lambdaQuery()
                    .in(!CollectionUtils.isEmpty(queryUserIds), Users::getId, queryUserIds)
                    .orderByDesc(Users::getUpdateDate)
                    .page(Page.of(dto.getPageNum(), dto.getPageSize()));
        }
        List<Long> userIds = page.getRecords().stream().map(Users::getId).toList();
        Map<Long, List<Long>> roleMap = userRoleService.lambdaQuery()
                .in(UserRole::getUserId, userIds)
                .list()
                .stream()
                .collect(Collectors.groupingBy(
                        UserRole::getUserId,
                        Collectors.mapping(UserRole::getRoleId, Collectors.toList())
                ));
        Map<Long, List<Long>> groupMap = userGroupService.lambdaQuery()
                .in(UserGroup::getUserId, userIds)
                .list()
                .stream()
                .collect(Collectors.groupingBy(
                        UserGroup::getUserId,
                        Collectors.mapping(UserGroup::getGroupId, Collectors.toList())
                ));
        /*
        IPage<QueryUserDTO> iPage = page.convert(users -> {
            QueryUserDTO queryUserDTO = usersConverter.toDto(users);
            List<UserRole> userRoles = userRoleService.lambdaQuery().eq(UserRole::getUserId, users.getId()).list();
            List<Long> roleIds = userRoles.stream().map(UserRole::getRoleId).toList();
            queryUserDTO.setRoleIds(roleIds);
            List<UserGroup> userGroups = userGroupService.lambdaQuery().eq(UserGroup::getUserId, users.getId()).list();
            List<Long> groupIds = userGroups.stream().map(UserGroup::getGroupId).toList();
            queryUserDTO.setGroupIds(groupIds);
            return queryUserDTO;
        });
        */
        IPage<QueryUserDTO> iPage = page.convert(users -> {
            QueryUserDTO queryUserDTO = usersConverter.toDto(users);
            List<Long> roleIds = roleMap.get(users.getId());
            queryUserDTO.setRoleIds(roleIds);
            List<Long> groupIds = groupMap.get(users.getId());
            queryUserDTO.setGroupIds(groupIds);
            return queryUserDTO;
        });
        return PageUtil.pageDto(iPage);
    }
}
